/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package model;


import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
import javax.faces.model.SelectItem;
import org.primefaces.model.DefaultTreeNode;
import org.primefaces.model.TreeNode;


/**
 *
 * @author ihabali
 */
public class BrowserModel implements Serializable{
    
    private final static long  serialVersionUID = -2638229452568302325L;
    private transient  Package[] packages ;
    private  String[] packagesNames ;
    private Map <String,String> packagesNamesList;
    private String packageToSearch;
    private Map<String,String> searchHistory ;
    
    private String searchItem;
    private int numberOfPackages ;
    private boolean isClassNotFoundException = false;
    private String errorClass;
    private Class currentClass ;
    private TreeMap<String,String> sorted_map;
    private Class tempClass;
    
    private ArrayList<DefaultTreeNode> treeMainLeafsList ;
    private DefaultTreeNode root ;  
    private DefaultTreeNode objectClassNode ; 
    private DefaultTreeNode selectedNode ;
    
    private int numberOfClass ;
    
     private transient Field[] fields ;
     private transient Map<String,Field> fieldsStrings;
     private transient TreeMap<String,Field> fieldsKeysAndValues;
     
     private transient Constructor[] constructors;
     private transient Map<String,Constructor>constructorsStrings;
     private transient TreeMap<String,Constructor> constructorsKeysAnsValues ;
     
     private transient Method[] methods;
     private transient Map<String,Method> methodsStrings;
     private transient TreeMap<String,Method> methodsKeysAndValues ;
     
     private int membersCount = 0;
     private  ArrayList<String> searchHistoryStrings;
     private String radioOption = "0";
   
     private boolean allMemebers = true;
     private boolean onlyConstructors = false;
     private boolean  onlyFields = false;
     private boolean onlyMethods = false ;
     private Object memeberSelected;
     private String declaration ;
     
     private boolean instance = true;
     private boolean staticButton = true;
     
     private boolean defaultButton = true;
     private boolean publicButton = true;
     private boolean privateButton =  true;
     private boolean protectedButton = true;
     
     private boolean abstrButton =true;
     private boolean syncButton =true;
     private boolean finalButton = true;
     private boolean throwsButton = true;
     private boolean nativeButton = true;
     private boolean transiButton =true;
     private boolean stricButton =true;
     private boolean volatileButton =true;
    
    
    public BrowserModel() throws ClassNotFoundException {
        
        packageToSearch ="java.lang";
        searchHistory = new HashMap<>();
        searchHistory.put("java.lang.Object","java.lang.Object" );
        searchItem ="java.lang.Object";
        currentClass = Class.forName("java.lang.Object");
        packagesNamesList =  new HashMap<>();
       ValueComparator bvc =  new ValueComparator(packagesNamesList);
        sorted_map = new TreeMap<>(bvc);
        
        packages = Package.getPackages();
        packagesNames = new String[packages.length];
        System.out.println(packages.length);
        numberOfPackages = packages.length;
        for (Package p : packages) {
            this.packagesNamesList.put(p.getName(), p.getName());
        }
        this.root = new DefaultTreeNode("Root" ,null);
       this.objectClassNode =  new DefaultTreeNode("Object",root);
       this.sorted_map.putAll(packagesNamesList);
       selectedNode = objectClassNode;
       setSelectedNode(objectClassNode);
       numberOfClass = 1;
       treeMainLeafsList = new ArrayList<>();
       treeMainLeafsList.add(this.objectClassNode);
       searchHistoryStrings = new ArrayList();
       fieldsStrings =  new HashMap<>();
       methodsStrings =  new HashMap<>();
       constructorsStrings = new HashMap<>();
//       ValueComparer constructor =  new ValueComparer (constructorsStrings);
//       ValueComparer  method =  new ValueComparer (methodsStrings);
//       ValueComparer  field =  new ValueComparer (fieldsStrings);
       constructorsKeysAnsValues = new TreeMap<>();
       fieldsKeysAndValues =  new TreeMap<>();
       methodsKeysAndValues = new TreeMap<>();
       this. populateMembers();
   }

    
    public TreeMap<String, String> getSorted_map() {
        return sorted_map;
    }
    public void setPackageToSearch() {
        if(currentClass != null){
           System.out.println("******* new Package Set**********");
           packageToSearch = currentClass.getPackage().getName();
        }
        
    }
    

   public String getPackageToSearch() {
        return packageToSearch;
    }

    
    public boolean classFoundException(String nameToSearch)throws ClassNotFoundException{
        try {
            currentClass= Class.forName(nameToSearch);
        } catch (ClassNotFoundException ex) {
            
            return true; 
            
        }
        return false;
        
    }
    
   


    
     public boolean isClassNotFoundException() {
        return isClassNotFoundException;
    }
     
     
     public Map<String, String> getSearchHistory() {
        return searchHistory;
    }

    

    public String getSearchItem() {
        return searchItem;
    }

    public void setSearchItem(String searchItem) {
        try {
            this.isClassNotFoundException = this.classFoundException(searchItem);
        } catch (ClassNotFoundException ex) {

        }
        if (!this.isClassNotFoundException) {
            this.searchItem = searchItem;
         if (!searchHistory.containsKey(searchItem)) {
                searchHistory.put(searchItem, searchItem);
                System.out.println("new value was set");
            }else{
             System.out.println("value Already in search history");
         }
        }else {
                 System.out.println("value rejected");
                errorClass = searchItem;
        }
    }
    
   public String getErrorClass() {
        return errorClass;
    }
   
    public void populateMembers (){
     
      fieldsStrings.clear();
      methodsStrings.clear();
      constructorsStrings.clear();
      
      membersCount =0;
      fields = currentClass.getDeclaredFields();
      methods = currentClass.getDeclaredMethods();
      constructors = currentClass.getDeclaredConstructors();
      
      
      for (Field f : fields){
          if(checkFieldsAgainstModfiers(f))
             fieldsStrings.put(f.getName(),f);
      }
      Class[] args;
      String argString = "";
        for (Method m : methods) {
            if (checkMethodsAgainstModfiers(m)) {
                if (m.getParameterTypes().length != 0) {
                    args = m.getParameterTypes();
                    for (Class c : args) {
                        argString = argString + " " + c.getSimpleName();
                    }

                    methodsStrings.put(m.getName() + "(" + argString + ")", m);
                    argString = "";
                } else {
                    methodsStrings.put(m.getName() + "()", m);
                }

            }
        }
       argString ="";
       args = null;
       for (Constructor c : constructors){
           if(checkConstructorsAgainstModfiers(c)){
           if(c.getParameterTypes().length != 0){
                args = c.getParameterTypes();
                for(Class argu : args){
                   argString = argString +" "+argu.getSimpleName();
               }
                constructorsStrings.put(currentClass.getSimpleName()+"("+argString+")",c);
                argString="";
           }else{
            constructorsStrings.put(currentClass.getSimpleName()+"()",c);
           }
       }
        
       }
       
        setConstructorsKeysAnsValues(constructorsStrings);
        setFieldsKeysAndValues(fieldsStrings);
        setMethodsKeysAndValues(methodsStrings);
        
    }

    public int getMembersCount() {
        return membersCount;
        
    }

    public void setMembersCount(int membersCount) {
        this.membersCount = membersCount;
    }
    
    public String getRadioOption() {
        return radioOption;
    }

    public void setRadioOption(String radioOption) {
        System.out.println(" new radio button value ");
        membersCount = 0;
        this.radioOption = radioOption;
        
        if(this.radioOption.equals ("0")){
            System.out.println(" radio button is" + radioOption);
            this.allMemebers = true;
            
        }else if(this.radioOption.equals ("1")){
            System.out.println(" radio button is" + radioOption);
            this.allMemebers = false;
            this.onlyFields = true;
            this.onlyMethods =false;
            this.onlyConstructors = false;
            
                    
        }else if(this.radioOption.equals ("2")){
            System.out.println(" radio button is" + radioOption);
            this.allMemebers = false;
            this.onlyFields = false;
            this.onlyMethods =false;
            this.onlyConstructors = true;
        }else if(this.radioOption.equals ("3")){
            System.out.println(" radio button is" + radioOption);
            this.allMemebers = false;
            this.onlyFields = false;
            this.onlyMethods =true;
            this.onlyConstructors = false;
        }
        
    }
    
 
    
    public boolean isInSearchHistory(String str){
        return this.searchHistoryStrings.contains(str);
    }
   public void createClassHeiraracy() {
      ArrayList<DefaultTreeNode> tempTreessList =  new ArrayList<>();
        DefaultTreeNode currentTree = objectClassNode;
        int count = 0;
        tempClass = currentClass;
        DefaultTreeNode tempNode;
        int previousSize =0;
        ArrayList<String> list = new ArrayList<>();
        
        while (tempClass != null) {
            list.add(0, tempClass.getSimpleName());// add to front
            tempClass = tempClass.getSuperclass();
        }
        
        
           //lets try find the root of this class in the class heierarcy display
        String temp;
        if(list.size()>1){
           temp = list.get(1); 
        }else{
            setSelectedNode(objectClassNode);
            return;
        }
           
           for(DefaultTreeNode dtn : this.treeMainLeafsList){
                if( ((String)dtn.getData()).equals(temp)){
                   //we found a tree node this class belongs too
                   currentTree = (DefaultTreeNode)dtn;
              }
           }
           
          if (((String) currentTree.getData()).equals("Object")) {
           //we didn't find anything that this class beongs too , so the root is Object
              list.remove(0);
              for (String str : list) {
                  if (count == 0) {
                      tempTreessList.add(new DefaultTreeNode(str, objectClassNode));
                      tempTreessList.get(count).setExpanded(true);
                      tempTreessList.get(count).setSelectable(true);
                      treeMainLeafsList.add(tempTreessList.get(count));
                      searchHistoryStrings.add(str);
                      numberOfClass++;
                      count++;
                  } else {
                      tempTreessList.add(new DefaultTreeNode(str, tempTreessList.get(count - 1)));
                      tempTreessList.get(count).setExpanded(true);
                      tempTreessList.get(count).setSelectable(true);
                      searchHistoryStrings.add(str);
                      count++;
                      numberOfClass++;
                  }
                 
              }
           
           setSelectedNode(tempTreessList.get(count-1));
           
       } else {
               //we have to search the tree to find the parent of this class  .
               //we have to remove the duplicate from the new tree and attach the rest to the existing tree 
              previousSize =list.size();
              for (String str : list){
                  for (TreeNode tn : currentTree.getChildren()){
                      if(((String)tn.getData()).equals(str)){
                          list.remove(str);
                    }
                  }
              }
              count = 0 ;
              if(!list.isEmpty()){
                  tempNode = (DefaultTreeNode) currentTree.getChildren().get(previousSize-list.size());//the parent of the subtree
                   tempTreessList.add(tempNode);
                  for (String str : list) {
                   if (count == 0){
                   tempTreessList.add(new DefaultTreeNode(str, tempNode));
                   tempTreessList.get(count).setSelectable(true);
                   tempTreessList.get(count).setExpanded(true);
                   searchHistoryStrings.add(str);
                   numberOfClass++;
                   count++;
                   }else {
                   tempTreessList.add(new DefaultTreeNode(str, tempTreessList.get(count-1)));
                   tempTreessList.get(count).setSelectable(true);
                   tempTreessList.get(count).setExpanded(true);
                   count++;
                   searchHistoryStrings.add(str);
                   numberOfClass++;
                   }
                  }
              }
              setSelectedNode(tempTreessList.get(count-1));
          }
   }

    public TreeNode getRoot() {
        return root;
    }

   

    public TreeNode getSelectedNode() {
        return selectedNode;
    }

    public void setSelectedNode(DefaultTreeNode selectedNode) {
        if (selectedNode != null){
            this.selectedNode.setSelected(false);
            selectedNode.setSelectable(true);
            selectedNode.setExpanded(true);
            selectedNode.setSelected(true);
            this.selectedNode = selectedNode;
        }
        
    }

    public int getNumberOfPackages() {
        return numberOfPackages;
    }

    public void setNumberOfPackages(int numberOfPackages) {
        this.numberOfPackages = numberOfPackages;
    }

    public int getNumberOfClass() {
        return numberOfClass;
    }

    public void setNumberOfClass(int numberOfClass) {
        this.numberOfClass = numberOfClass;
    }

    public TreeMap<String, Field> getFieldsKeysAndValues() {
        if(this.allMemebers || this.onlyFields){
           
            if(fieldsStrings != null){
                fieldsKeysAndValues.putAll(fieldsStrings);
                 membersCount += fieldsStrings.size();
               return fieldsKeysAndValues;
            }
        }
       
            return null;
    }

    public TreeMap<String, Constructor> getConstructorsKeysAnsValues() {
        if (this.allMemebers || this.onlyConstructors) {
            if (constructorsStrings != null) {
                membersCount += constructorsStrings.size();

                constructorsKeysAnsValues.putAll(constructorsStrings);

                return constructorsKeysAnsValues;
            }
        }
        return null;
    }

    public TreeMap<String, Method> getMethodsKeysAndValues() {
        if(this.allMemebers || this.onlyMethods){
          if(methodsStrings != null){
           membersCount += methodsStrings.size();
           methodsKeysAndValues.putAll(methodsStrings);
           return methodsKeysAndValues;
            }
       }
       
           return null;
    }

    public void setFieldsKeysAndValues(Map<String, Field> fieldsKeysAndValues) {
        this.fieldsKeysAndValues.putAll(fieldsKeysAndValues); 
    }

    public void setConstructorsKeysAnsValues(Map<String, Constructor> constructorsKeysAnsValues) {
        this.constructorsKeysAnsValues.putAll( constructorsKeysAnsValues);
    }

    public void setMethodsKeysAndValues(Map<String, Method> methodsKeysAndValues) {
        this.methodsKeysAndValues.putAll(methodsKeysAndValues) ;
    }

    

    public String getDeclaration() {
        return declaration;
    }

    public void setDeclaration(String declaration) {
        this.declaration = declaration;
    }

   
   public void methodSelected (Method m){
       this.setDeclaration(m.toGenericString());
       memeberSelected = m;
   }
   
   public void constructorSelected (Constructor c){
       this.setDeclaration(c.toGenericString());
       memeberSelected = c;
   }
   
   public void fieldSelected (Field f){
       this.setDeclaration(f.toGenericString());
       memeberSelected = f;
   }

    public Object getMemeberSelected() {
        return memeberSelected;
    }

    public void setMemeberSelected(Object memeberSelected) {
        this.memeberSelected = memeberSelected;
    }

    public Class getCurrentClass() {
        return currentClass;
    }
    
   
    class ValueComparator implements Comparator<String> , Serializable{

    Map<String,String > base;
    public ValueComparator(Map map){
        base = map;
    }
// Note: this comparator imposes orderings that are inconsistent with equals.    
    @Override
    public int compare(String a, String b) {
          // returning 0 would merge keys
          return base.get(a).compareTo(b) ;
            
         
    }
}

    public boolean isStaticButton() {
        return staticButton;
    }

    public void setStaticButton(boolean staticButton) {
        this.staticButton = staticButton;
    }

    public boolean isDefaultButton() {
        return defaultButton;
    }

    public void setDefaultButton(boolean defaultButton) {
        this.defaultButton = defaultButton;
    }

    public boolean isPublicButton() {
        return publicButton;
    }

    public void setPublicButton(boolean publicButton) {
        this.publicButton = publicButton;
    }

    public boolean isPrivateButton() {
        return privateButton;
    }

    public void setPrivateButton(boolean privateButton) {
        this.privateButton = privateButton;
    }

    public boolean isProtectedButton() {
        return protectedButton;
    }

    public void setProtectedButton(boolean protectedButton) {
        this.protectedButton = protectedButton;
    }

    public boolean isAbstrButton() {
        return abstrButton;
    }

    public void setAbstrButton(boolean abstrButton) {
        this.abstrButton = abstrButton;
    }

    public boolean isSyncButton() {
        return syncButton;
    }

    public void setSyncButton(boolean syncButton) {
        this.syncButton = syncButton;
    }

    public boolean isFinalButton() {
        return finalButton;
    }

    public void setFinalButton(boolean finalButton) {
        this.finalButton = finalButton;
    }

    public boolean isThrowsButton() {
        return throwsButton;
    }

    public void setThrowsButton(boolean throwsButton) {
        this.throwsButton = throwsButton;
    }

    public boolean isNativeButton() {
        return nativeButton;
    }

    public void setNativeButton(boolean nativeButton) {
        this.nativeButton = nativeButton;
    }

    public boolean isTransiButton() {
        return transiButton;
    }

    public void setTransiButton(boolean transiButton) {
        this.transiButton = transiButton;
    }

    public boolean isStricButton() {
        return stricButton;
    }

    public void setStricButton(boolean stricButton) {
        this.stricButton = stricButton;
    }

    public boolean isVolatileButton() {
        return volatileButton;
    }

    public void setVolatileButton(boolean volatileButton) {
        this.volatileButton = volatileButton;
    }

    public boolean isInstance() {
        return instance;
    }

    public void setInstance(boolean instance) {
        this.instance = instance;
    }
    
    
    public boolean checkConstructorsAgainstModfiers(Constructor c){
        
        if (!staticButton){
            return !c.toGenericString().contains("static");
        }
        
        if(!instance){
            return c.toGenericString().contains("static");
        } 
        
        if(!protectedButton){
            return !c.toGenericString().contains("protected");
        }
        
        if(!privateButton){
           return  !c.toGenericString().contains("private");
        }
        if(!publicButton){
            return !c.toGenericString().contains("public");
            
        } 
        
        if (!defaultButton){
            return !c.toGenericString().contains("default");
        }
        if (!syncButton){
           return !c.toGenericString().contains("synchronized" ); 
        }    
        if(!abstrButton){
             return !c.toGenericString().contains("abstract"); 
        }
        if(!finalButton){
             return !c.toGenericString().contains("final"); 
        }
        if (!throwsButton){
            return !c.toGenericString().contains("throws"); 
        }
        
        if(!nativeButton){
          return !c.toGenericString().contains("native");    
        }
        if(!transiButton){
            
            return !c.toGenericString().contains("transient "); 
        }
        
        if(!stricButton){
            return !c.toGenericString().contains("strict"); 
        }
        
        if(!volatileButton){
            return !c.toGenericString().contains("volatile"); 
        }
        
        return true;
    }
    
    public boolean checkFieldsAgainstModfiers(Field f){
        if (!staticButton){
            return !f.toGenericString().contains("static");
        }
        
        if(!instance){
          return f.toGenericString().contains("static");    
        }
        
        if(!protectedButton){
         return !f.toGenericString().contains("protected");   
        }
        
        if(!privateButton){
            return !f.toGenericString().contains("private");
        }
        if(!publicButton){
          return !f.toGenericString().contains("public");    
        } 
        
        if (!defaultButton){
            return !f.toGenericString().contains("default");
        }
        if (!syncButton){
            return !f.toGenericString().contains("synchronized");
        }    
        if(!abstrButton){
            return !f.toGenericString().contains("abstract");
        }
        if(!finalButton){
            return !f.toGenericString().contains("final");
        }
        if (!throwsButton){
            return !f.toGenericString().contains("throws");
        }
        
        if(!nativeButton){
            return !f.toGenericString().contains("native");
        }
        if(!transiButton){
            return !f.toGenericString().contains("transient");
        }
        
        if(!stricButton){
            return !f.toGenericString().contains("strict");
        }
        
        if(!volatileButton){
            return !f.toGenericString().contains("volatile");
        }
        
        return true;
    }
    
    public boolean checkMethodsAgainstModfiers(Method m){
        if (!staticButton){
            return !m.toGenericString().contains("static");
        }
        
        if(!instance){
          return m.toGenericString().contains("static");    
        }
        
        if(!protectedButton){
         return !m.toGenericString().contains("protected");   
        }
        
        if(!privateButton){
            return !m.toGenericString().contains("private");
        }
        if(!publicButton){
          return !m.toGenericString().contains("public");    
        } 
        
        if (!defaultButton){
            return !m.toGenericString().contains("default");
        }
        if (!syncButton){
            return !m.toGenericString().contains("synchronized");
        }    
        if(!abstrButton){
            return !m.toGenericString().contains("abstract");
        }
        if(!finalButton){
            return !m.toGenericString().contains("final");
        }
        if (!throwsButton){
            return !m.toGenericString().contains("throws");
        }
        
        if(!nativeButton){
            return !m.toGenericString().contains("native");
        }
        if(!transiButton){
            return !m.toGenericString().contains("transient");
        }
        
        if(!stricButton){
            return !m.toGenericString().contains("strict");
        }
        
        if(!volatileButton){
            return !m.toGenericString().contains("volatile");
        }
        
        return true;
    }

}
    
    
    

